#include "manager_file.h"

#include "public_define.h"
#include "eventloop.h"
#include "TcpClientInstance.h"
#include "tool.h"
#include "data_build.h"
#include "antianaphylaxis_queue.h"
#include "filemanagercommand.h"
#include "filemanager.h"
#include "dat_parser.h"
#include "desensitize.h"

uint32_t g_ba626_num = 0;
const uint32_t ba626_gap = 5;
const std::string ba626_topic = "ad_algo_ins-localization.InsData-Du";


using namespace DcAdr;
ManagerFile::ManagerFile()
{
}

ManagerFile::~ManagerFile()
{
}

ManagerFile &ManagerFile::getInstance()
{
    static ManagerFile instance;
    return instance;
}

void ManagerFile::openFile(std::map<std::string, std::vector<ProtoData>> &pieceData, const TriggerEventInfo &trigger)
{
    auto func = [=]()
    {
        openFilePrivate(pieceData, trigger);
    };
    appendEvent(ThreadType::E_InFileCacehThread, func);
}

void ManagerFile::openFilePrivate(const std::map<std::string, std::vector<ProtoData>> &pieceData, const TriggerEventInfo &trigger)
{
    bool isIT088 = false;
    if (!trigger.eventName.compare(IT088_ONE))
    {
        isIT088 = true;
        
        auto func = [=]()
        {
            TcpClientInstance::getInstance().sendStart(pieceData, trigger);
        };

        
        appendEvent(ThreadType::E_VideoSend, func);

    }
	
    //存在此文件，暂不记录，返回
    if (Contain_Map(m_eventFileAll, trigger.eventName))
    {
        return;
    }

	if(trigger.eventName == CSMAP_3)
		g_ba626_num = ba626_gap;
	
    EventFile_t eventFile;
    eventFile.trig = trigger;
    //将数据先写入文件
    for (auto &data : pieceData)
    {
        std::string fileType = data.first;

        if (isIT088 && !fileType.compare(DAT_FILE_TYPE_VIDEO))//IT088_ONE 事件不写视频文件
        {
            continue;
        }
        
        std::shared_ptr<DataRecord> datRecorder = make_shared<DataRecord>();
        if(!trigger.eventName.compare(CSMAP_4))
        {
            Log_Info("gd write file  ,filepath {}",global_data::g_file_path+trigger.eventName+global_data::g_normal_collect__fenceType);
            string normal_collect__filename = datRecorder->initializeRecord(global_data::g_file_path, trigger.eventName+global_data::g_normal_collect__fenceType, fileType, trigger.recordTime,trigger.triggerTime,trigger.triggerStartTime); 
            global_data::g_normal_collect__filename.push_back(normal_collect__filename);
            Log_Info("gd write file filename {}",normal_collect__filename);
        }else{
            datRecorder->initializeRecord(global_data::g_file_path, trigger.eventName, fileType, trigger.recordTime,trigger.triggerTime,trigger.triggerStartTime);
        }
        for (auto &tData : data.second)
        {
        	if((eventFile.trig.eventName == CSMAP_3) && (*tData.topic == ba626_topic))// BA626 每5帧保留一帧
			{	
				g_ba626_num ++;
				if(g_ba626_num < ba626_gap)
					continue;
				else
					g_ba626_num = 0;
			}
            datRecorder->record(*tData.topic, tData.timestamp, *tData.data);
        }
        eventFile.fileHandle[fileType] = datRecorder;
    }
    m_eventFileAll[trigger.eventName] = eventFile;
    updateCollectTopicSet();
}

void ManagerFile::closeFile(const TriggerEventInfo &trigger)
{
    auto func = [=]()
    {
        closeFilePrivate(trigger);
    };
    appendEvent(ThreadType::E_InFileCacehThread, func);
}

void ManagerFile::closeFilePrivate(const TriggerEventInfo &trigger)
{
    //没有记录，返回
    if (!Contain_Map(m_eventFileAll, trigger.eventName))//事件文件池
    {
        return;
    }
    //获取所有文件名
    std::map<string, string> filePaths;
    for (auto &iter : m_eventFileAll)
    {
        if (iter.first != trigger.eventName)
        {
            continue;
        }
        for (auto &fileIter : iter.second.fileHandle)
        {
            if (nullptr == fileIter.second)
            {
                continue;
            }
            filePaths.insert({fileIter.first, fileIter.second->m_fileName});
        }
    }

    //去掉引用，让文件先存储
    m_eventFileAll.erase(trigger.eventName);
    updateCollectTopicSet();

    //营地不需要上传，取出落盘的数据，发送给EDC
    if (IT088_ONE == trigger.eventName)
    {
        std::string fileName_3d = filePaths[DAT_FILE_TYPE_MID]; // 3d视频是1类型
        uint64_t endFrameUtcTime  = CommonOprTime::getTime();

        auto func = [=]()
        {
            TcpClientInstance::getInstance().sendEnd(trigger, fileName_3d, endFrameUtcTime);
        };
        appendEvent(ThreadType::E_VideoSend, func);
        Log_Info("shaota event end; taskid:{} fileName_3d:{};",trigger.taskId, fileName_3d);
        return;
    }

    //众包围栏超时删文件不上传
    if ((CSMAP_1 == trigger.eventName || CSMAP_2 == trigger.eventName || CSMAP_3 == trigger.eventName) && 
        (CommonOprTime::getTime() - trigger.recordTime > trigger.timeout))//条件同电子围栏超时
    {
        for (auto &oneFile : filePaths)
        {
            int ret=remove(oneFile.second.c_str());
            if(ret<0)
            {
                Log_Info("remove file fail,file:{}",oneFile.second);
            }
            Log_Info("event:{} timeout; delet :{};",trigger.eventName, oneFile.second);
        }
        return;
    }


    //BA692缓存文件信息，等待commit信号发事件信息
    if ("BA692" == trigger.eventName)
    {
        BA692File_t one = {trigger, filePaths};
        m_BA692Catch.push_back(one);
        return;
    }

    //做文件管理
    for (auto &oneFile : filePaths)
    {
        // std::vector<int> vecEventid;
        uint8_t fileType = 1;
        (void)fileType;
        string newSaveFileName = FileManagerCommand::getInstance()->saveFile(trigger.level, oneFile.second);
        Log_Info("[filemanage] old:{} new:{}", oneFile.second, newSaveFileName);

        if (!(newSaveFileName.empty()))
        {
            oneFile.second = newSaveFileName;
        }
        else
        {
            Log_Info("[filemanage] rename faild; old file:{}", oneFile.second);
        }
    }

    //其他时间传至云端
    packageEventMessage(filePaths, trigger);
}

void ManagerFile::SendBA692Info()
{
    auto func = [=]()
    {
        if (m_BA692Catch.size() == 0)
        {
            return;
        }

        Log_Info("m_BA692Catch num:{}", m_BA692Catch.size());
        for (auto &oneCach : m_BA692Catch)
        {
            //做文件管理
            for (auto &oneFile : oneCach.filePaths)
            {
                // std::vector<int> vecEventid;
                uint8_t fileType = 1;
                (void)fileType;
                string newSaveFileName = FileManagerCommand::getInstance()->saveFile(oneCach.trig.level, oneFile.second);
                Log_Info("[filemanage] old:{} new:{}", oneFile.second, newSaveFileName);

                if (!(newSaveFileName.empty()))
                {
                    oneFile.second = newSaveFileName;
                }
                else
                {
                    Log_Info("[filemanage] rename faild; old file:{}", oneFile.second);
                }
            }

            //其他事件传至云端
            packageEventMessage(oneCach.filePaths, oneCach.trig);
        }

        m_BA692Catch.clear();
    };
    appendEvent(ThreadType::E_InFileCacehThread, func);
}

//每秒检查缓存的BA692文件是否过期，如果过期删除
void ManagerFile::CheckBA692CommitEnd()
{
    if (m_BA692Catch.size() == 0)
    {
        return;
    }

    auto func = [=]()
    {
        auto oneCach_Ptr = m_BA692Catch.begin();
        uint64_t nowTime = CommonOprTime::getTime();

        if (nowTime < oneCach_Ptr->trig.recordTime || (nowTime - oneCach_Ptr->trig.recordTime) > 40 * 1000)
        {            
            for (auto &oneFile : oneCach_Ptr->filePaths)
            {
                int retCI = remove(oneFile.second.c_str());
                Log_Info("remove:{} retCI:{}", oneFile.second, retCI);
            }

            m_BA692Catch.erase(oneCach_Ptr);
        }
    };
    appendEvent(ThreadType::E_InFileCacehThread, func);
}

void ManagerFile::writeFile(const ProtoData &data)
{
#if 0
    // 由于data只有低频次需要落盘，每次都调用线程比较吃资源
    // 因此这里做个快速的判断过滤调不需要落盘的数据
    if (!m_collectTopicSet.count(*data.topic)) {
        return;
    }
#endif

    auto func = [=]()
    {
        writeFilePrivate(data);
    };
    appendEvent(ThreadType::E_InFileCacehThread, func);
}

void ManagerFile::writeFilePrivate(const ProtoData &data)
{
    // 由于data只有低频次需要落盘，每次都调用线程比较吃资源
    // 因此这里做个快速的判断过滤调不需要落盘的数据
    if (!m_collectTopicSet.count(*data.topic)) {
        return;
    }
    
    std::string fileType = DataRecord::getFileNameType(*data.topic);
    for (auto &eventName : m_eventFileAll)
    {
        EventFile_t &eventFile = m_eventFileAll[eventName.first];
        if (!Contain_List(eventFile.trig.collectDatas, *data.topic))
        {
            continue;
        }
        std::shared_ptr<DataRecord> datRecorder = eventFile.fileHandle[fileType];
        if (nullptr == datRecorder || nullptr == datRecorder.get())
        {
            if (eventFile.trig.eventName == IT088_ONE && fileType == DAT_FILE_TYPE_VIDEO)
            {
                auto func = [=]()
                {
                    TcpClientInstance::getInstance().pareOneFrame(data);
                };
                appendEvent(ThreadType::E_VideoSend, func);

                continue;//IT088_ONE 不写视频文件
            }
            
            datRecorder = make_shared<DataRecord>();
            datRecorder->initializeRecord(global_data::g_file_path, eventName.first, fileType, eventName.second.trig.recordTime,eventName.second.trig.triggerTime,eventName.second.trig.triggerStartTime);


            eventFile.fileHandle[fileType] = datRecorder;

            Log_Info("zjs_tmp_0511; create datRecorder when write; eventname:{}", eventName.first);


            // return;//刚起来就触发事件，视频数据后面到，来一帧就会构造，析构datRecorder //zjs_todo;触发前没有数据，后来了数据记录不上。
        }

		if((eventFile.trig.eventName == CSMAP_3) && (*data.topic == ba626_topic))// BA626 每5帧保留一帧
		{	
			g_ba626_num ++;
			if(g_ba626_num < ba626_gap)
				continue;
			else
				g_ba626_num = 0;
		}
        datRecorder->record(*data.topic, data.timestamp, *data.data);
    }
}

bool eventDesensitizeFile(const char* fileName, Json::Value &fileJson,Json::Value &fileArray)
{
    uint64_t fileSize = CommonOprSystem::getFileSizeDigit(fileName);
    Log_Info("gd Desensitize File filename: {} ,filesize: {} ",fileName,fileSize)
    if(fileSize<4*1024*1024)
    {
            //对文件进行加密操作
        if(0!=car_scheduler::Desensitize::getInstance().DesensitizeFile(string(fileName),string(fileName)+".des"))
        {
            Log_Info("gd DesensitizeFile failed");
            return false;
        }
        //.zip.des
        string fileWithPathDes =string(fileName)+".des";
        //string fileWithPath = "/log/capilot/capilot_dump.txt";
        fileJson["controllerCode"] = global_data::g_controler_code;
        string fileNameDes = Util::getFileBaseName(fileWithPathDes);
        fileJson["fileName"] = fileNameDes;
        fileJson["filePath"] = fileWithPathDes;
        fileArray.append(fileJson);
        Log_Info("gd DesensitizeFile success");
        return true;
    }else
    {
        Log_Info("gd File is outsize ,DesentizeFile failed!!!, filename: {} ,filesize: {} ",fileName,fileSize);
        return false;
    }  
}

void ManagerFile::packageEventMessage(const std::map<std::string, std::string> &filePaths, const TriggerEventInfo &trigger)
{
    Json::Value root;
    Json::Value header;
    Json::Value param;
    Json::Value data;

    //得到请求ID
    std::string reqId = "";
    getReqID(reqId);

    header["v"] = Json::Value("1.0.0");
    header["r"] = Json::Value(reqId); // 1463106055567 time ms
    header["sk"] = Json::Value("Et_CarData");
    header["m"] = Json::Value("Et_EventFile"); //
    header["a"] = Json::Value(1);              //
    header["e"] = Json::Value(0);              //

    param["ec"] = trigger.eventName;
    uint64_t time = trigger.recordTime;
    param["ut"] = time;
    param["ts"] = time;
    param["ep"] = "1";
    param["Lng"] = global_data::g_car_location_lng;
    param["Lat"] = global_data::g_car_location_lat;

    Json::Value fileArray;
    for (auto &filePair : filePaths)
    {
        string fileWithPath = filePair.second;
        Json::Value fileJson;
        fileJson["controllerCode"] = global_data::g_controler_code;
        string fileName = Util::getFileBaseName(fileWithPath);

        if ((filePair.first == DAT_FILE_TYPE_VIDEO) && (global_data::g_isUploadDesensFile))
        {
            //类型为DAT_FILE_TYPE_VIDEO 表示是脱敏前视频，并且开启了要上传脱敏文件功能，发送脱敏后的文件信息上云
            string desensFileName;
            DataRecord::convertDesensFiletype(fileName, desensFileName);
            fileJson["fileName"] = desensFileName;
            string tmpfilepath=Util::getFilePath(fileWithPath);
            string desensFileWithPath = tmpfilepath + desensFileName;//将脱敏后文件与脱敏前文件应该在同一目录下面
            Log_Info("[xiangrui debug] desensFileWithPath:{}",desensFileWithPath);
            fileJson["filePath"] = desensFileWithPath;

            //将此文件信息写入待脱敏文件
            auto func = [=]()
            {
                AntianaphylaxisQueue::getInstance().writeFileName(fileWithPath, desensFileWithPath,trigger.level);
            };
            appendEvent(ThreadType::E_InVideoAntianaphylaxis, func);
            fileArray.append(fileJson);
        }
        else
        {
            fileJson["fileName"] = fileName;
            fileJson["filePath"] = fileWithPath;
            if(filePair.first == DAT_FILE_TYPE_MID && trigger.eventName=="BA625")//BA625 文件1需要进行压缩后加密上传
            {
                //先对dat文件进行压缩
                Log_Info("gd log compress trigger BA625 dat \n");
                std::string cmd="zip " +fileWithPath+".zip " +fileWithPath;
                int ret=std::system(cmd.c_str());
                if(ret!=0)
                {
                    Log_Info("gd cmd fail :{}",cmd);
                }
                Log_Info("gd cmd zip:{}",cmd);
                std::string zipFileName=fileWithPath+".zip";
                if(!car_scheduler::Desensitize::getInstance().getHasInitSdk())
                {
                    Log_Info("gd DesensitizeFile init failed");
                    continue;
                }

                if(!eventDesensitizeFile(zipFileName.c_str(),fileJson,fileArray))
                {
                    Log_Info("gd BA625 DesensitizeFile failed");
                    continue;
                }
                Log_Info("gd BA625 DesensitizeFile sucess");
            }
            else if(filePair.first == DAT_FILE_TYPE_MID && trigger.eventName=="BA626")//BA626事件需要进行加密上传，不用压缩
            {    
                Log_Info("gd log DesensitizeFile trigger BA626 dat \n");   
                if(!car_scheduler::Desensitize::getInstance().getHasInitSdk())
                {
                    Log_Info("gd DesensitizeFile init failed");
                    continue;
                }
        
                if(!eventDesensitizeFile(fileWithPath.c_str(),fileJson,fileArray))
                {
                    Log_Info("gd BA626 DesensitizeFile failed");
                    continue;
                }
                Log_Info("gd BA626 DesensitizeFile sucess");
            }
            else
            {
                fileJson["fileName"] = fileName;
                fileJson["filePath"] = fileWithPath;
                fileArray.append(fileJson);
            }
        }
       
    }
    if(trigger.eventName=="BA620" || trigger.eventName=="BA621")
    {
        Json::Value fileJson;
        string fileWithPath = "/data/ca_platform/pdm/fault_info_upload.dat";
        fileJson["controllerCode"] = global_data::g_controler_code;
        string fileName = Util::getFileBaseName(fileWithPath);
        fileJson["fileName"] = fileName;
        fileJson["filePath"] = fileWithPath;
        fileArray.append(fileJson);
    }
    // 不生成dat文件，C3B改为拉取/log/app/capilot_dump.txt
    if(trigger.eventName=="BA687")
    {
        Json::Value fileJson;
        string fileWithPath = "/log/app/capilot_dump.txt";
        fileJson["controllerCode"] = global_data::g_controler_code;
        string fileName = Util::getFileBaseName(fileWithPath);
        fileJson["fileName"] = fileName;
        fileJson["filePath"] = fileWithPath;
        fileArray.append(fileJson);
    }

    for (auto &file : trigger.otherSysFile)
    {
        Json::Value fileJson;
        fileJson["controllerCode"] = file.controllerCode;
        fileJson["fileName"] = Util::getFileBaseName(file.fileName);
        fileJson["filePath"] = file.fileName;
        fileArray.append(fileJson);
    }
    param["fileCtx"] = fileArray;

    Json::Value extends;
    int accOdometer = (int)(global_data::g_extEvent.accOdometer/10);//转成km
    int iaccOdometer = (int)(global_data::g_extEvent.iaccOdometer/10);
    int ndaOdometer = (int)(global_data::g_extEvent.ndaOdometer/10);
    extends["accOdometer"] = accOdometer;
    extends["iaccOdometer"] = iaccOdometer;
    extends["ndaOdometer"] = ndaOdometer;
    
    extends["totalOdometer"] = ((double)global_data::g_extEvent.totalOdometer/10);
    extends["cdcTotMilg"] = accOdometer + iaccOdometer + ndaOdometer;

    extends["softVersion"] = CRecord::GetC2Version();
    if (!global_data::g_C2_bigVersion.empty())
    {
        extends["c2_software_version"] = global_data::g_C2_bigVersion;
    }    

    param["extendedFields"] = extends;

    // double data112 = JsonToValue<double>(extends["totalOdometer"]);
    // Log_Info("zjs_tmp_0815; data:{} g_too:{} convert:{}" ,data112, global_data::g_extEvent.totalOdometer, ((double)global_data::g_extEvent.totalOdometer/10));

    data["p"] = param;
    root["h"] = header;
    root["d"] = data;

    std::string sendStr = root.toStyledString();
    DataBuild::mqttsend(sendStr);
}

void ManagerFile::updateCollectTopicSet() {
    std::unordered_set<std::string> newSet;
    for (auto it: m_eventFileAll) {
        newSet.insert(it.second.trig.collectDatas.begin(), it.second.trig.collectDatas.end());
    }
    m_collectTopicSet.swap(newSet);
}
